/*
 * Copyright 2009 Armando Blancas
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package taskgraph.tasks.text;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import taskgraph.Config;
import taskgraph.ports.CharInputPort;
import taskgraph.ports.CharOutputPort;
import taskgraph.tasks.PrimitiveTask;


/**
 * Greps lines of text with the specified pattern.
 * How the pattern is used depends on the mode: 
 * <p>{@code FIND} - Finds the pattern as a substring.  
 * <p>{@code MATCH} - Determines if the line properly matches the regex pattern.
 * 
 * @author Armando Blancas
 * @see taskgraph.ports.CharInputPort
 * @see taskgraph.ports.CharOutputPort
 */
public class GrepLines extends PrimitiveTask {

	public enum GREP_F { FIND, MATCH };
	
	private static final int DEFAULT_CAPACITY = Config.get().bufferCapacity();
	
	private CharInputPort input;
	private CharOutputPort output;
	private String pattern;
	private GREP_F mode = GREP_F.FIND;
	private int bufferSize = DEFAULT_CAPACITY;

	/**
	 * This constructor allows creating instances as beans.
	 */
    public GrepLines() {
    	// do nothing
    }

	/**
     * Creates a fully configured instance.
     * 
     * @param output The char input port.
     */
    public GrepLines(final CharInputPort input, 
    		         final CharOutputPort output, 
    		         final String pattern) {
    	this(input, output, pattern, GREP_F.FIND);
    }

	/**
     * Creates a fully configured instance.
     * 
     * @param output The char input port.
     */
    public GrepLines(final CharInputPort input, 
    		         final CharOutputPort output, 
    		         final String pattern,
    		         final GREP_F mode) {
    	setInput(input);
    	setOutput(output);
    	setPattern(pattern);
    	setMode(mode);
    }

	/**
	 * Gets the buffer size for reading chunks of characters.
	 * 
	 * @return the bufferSize
	 */
	public int getBufferSize() {
		return bufferSize;
	}

    /**
     * Gets the input port.
     * 
	 * @return The char input port.
	 */
	public CharInputPort getInput() {
		return input;
	}

    /**
	 * Gets the mode for grep.
	 * 
	 * @return The {@code GREP_F} mode value.
	 */
	public GREP_F getMode() {
		return mode;
	}
	
    /**
     * Gets the output port.
     * 
	 * @return The char output port.
	 */
	public CharOutputPort getOutput() {
		return output;
	}

    /**
	 * Gets the pattern for grep.
	 * 
	 * @return The {@code String} pattern value.
	 */
	public String getPattern() {
		return pattern;
	}

	/**
	 * Performs the work of this task.
	 * 
	 * Reads lines from the input port, greps the pattern, and writes any 
	 * matching lines to the output port. Apply the grep mode for finding
	 * the pattern as a substring, or testing a regex match.
	 *<pre>
	 *while not EOF
	 *    read a line from input port
	 *    grep with given pattern
	 *    print line if pattern matches
	 *close input port
	 *close output port
	 *</pre>
	 */
	@Override
    public void run() {
    	try {
	        Pattern p = Pattern.compile(pattern);
    		while (!Thread.currentThread().isInterrupted()) {
    			String line = input.readLine();
    			if (line == null) {
    				break;
    			}
    	        Matcher m = p.matcher(line);
    	        if (mode == GREP_F.FIND) {
    	        	if (m.find()) {
    	        		output.println(line);    	        		
    	        	}
    	        } else {
    	        	if (m.matches()) {
    	        		output.println(line);
    	        	}
    	        }
            }
        } catch (InterruptedIOException ie) {
        	// interruption request
        } catch (IOException ioe) {
        	// pipe synch error
            log.error("GrepLines#run()", ioe);
        } finally {
			input.close();
			output.close();
        }
    }

	/**
	 * Sets the buffer size for reading chunks of characters.
	 * <p>The argument should not be less than the default. If it is, the
	 * default size is used instead.
	 * 
	 * @param bufferSize the bufferSize to set
	 */
	public void setBufferSize(final int bufferSize) {
		this.bufferSize = 
			(bufferSize < DEFAULT_CAPACITY) ? DEFAULT_CAPACITY : bufferSize;
	}

	/**
	 * Sets the input port.
	 * 
	 * @param input The char input port.
	 */
    public void setInput(final CharInputPort input) {
    	if (input == null)
    		throw new IllegalArgumentException("input == null");
 		this.input = input;
	}

	/**
	 * Sets the mode for grep.
	 * 
	 * @param mode The {@code GREP_F} value to set.
	 */
	public void setMode(GREP_F mode) {
    	if (mode == null)
    		throw new IllegalArgumentException("mode == null");
		this.mode = mode;
	}

    /**
	 * Sets the output port.
	 * 
	 * @param output The char output port.
	 */
    public void setOutput(final CharOutputPort output) {
    	if (output == null)
    		throw new IllegalArgumentException("output == null");
 		this.output = output;
	}

	/**
	 * Sets the pattern to grep with.
	 * 
	 * @param pattern The {@code String} pattern to set.
	 */
	public void setPattern(final String pattern) {
    	if (pattern == null)
    		throw new IllegalArgumentException("pattern == null");
		this.pattern = pattern;
	}

}
